#ifndef __ZLIB_CODER__
#define __ZLIB_CODER__

#include "bas.coder.h"
#include "../../lib3rd/zlib/zlib.h"

namespace xm
{
	namespace coder
	{
		class InflateCoderLL : public CoderLL<>, UnkBase
		{
		protected:
			typedef CoderLL<> BaseT;
			z_stream	_zstm;
		public:
			HRESULT init_class( IUnknown * , IUnknown * ) { return S_OK;}
			UNKNOWN_IMP1_(IKBCoderLL);
		public:
			InflateCoderLL()
			{
				memset( &_zstm, 0, sizeof(_zstm) );
			}
			STDMETHOD(Start)( IUnknown* pstm, LPCVOID lpConfig = 0, IKBCoderHook * hook = 0 )
			{
				RFAILED( BaseT::Start(pstm,hook) );
				int zr = inflateInit(&_zstm);
				if( zr != Z_OK ) return E_FAIL;
				return S_OK;
			}
			STDMETHOD(End)() 
			{
				inflateEnd(&_zstm);
				RFAILED( BaseT::End() );
				return S_OK;
			}
		protected:
			SIZE_T _CodeFrame( LPCVOID lpInBuf, SIZE_T cbIn, LPVOID lpOutBuf, SIZE_T & cbOut )
			{
				_zstm.avail_in = (uInt)cbIn;
				_zstm.next_in = (Bytef*)lpInBuf;
				_zstm.avail_out = (uInt)cbOut;
				_zstm.next_out = (Bytef*)lpOutBuf;
				int zr = inflate(&_zstm,Z_SYNC_FLUSH);
				if( zr != Z_OK && zr != Z_STREAM_END ) 
					return INVALID_CODE_SIZE;
				cbOut = cbOut - _zstm.avail_out;
				return cbIn - _zstm.avail_in;
			}
		};

		class DeflateCoderLL : public CoderLL<0x10000>, UnkBase
		{
		protected:
			typedef CoderLL<0x10000> BaseT;
			z_stream	_zstm;
		public:
			HRESULT init_class( IUnknown * , IUnknown * ) { return S_OK;}
			UNKNOWN_IMP1_(IKBCoderLL);
		public:
			DeflateCoderLL()
			{
				memset( &_zstm, 0, sizeof(_zstm) );
			}
			STDMETHOD(Start)( IUnknown* pstm, LPCVOID lpConfig = 0, IKBCoderHook * hook = 0 )
			{
				const cconf::zlib * cfg = (const cconf::zlib *)lpConfig;
				if( !cfg ) cfg = &cconf::DefalteDefualt;
				int lev = cfg->complev < 10 ? cfg->complev : 5;
				if( !lev ) lev = 7;
				RFAILED( BaseT::Start(pstm,hook) );
				int zr = deflateInit(&_zstm,lev);
				if( zr != Z_OK ) return E_FAIL;
				return S_OK;
			}
			STDMETHOD(End)() 
			{
				SIZE_T cbOut = sizeof(_out_buf);
				if( _CodeFrame(NULL,0, _out_buf, cbOut ) == INVALID_CODE_SIZE )
					return E_FAIL;
				RFAILED( __AfterDataProcessed(0,0,cbOut,_out_buf) );
				deflateEnd(&_zstm);
				RFAILED( BaseT::End() );
				return S_OK;
			}
		protected:
			SIZE_T _CodeFrame( LPCVOID lpInBuf, SIZE_T cbIn, LPVOID lpOutBuf, SIZE_T & cbOut )
			{
				int zflag = Z_NO_FLUSH;
				if( !lpInBuf && !cbIn )
					zflag = Z_FINISH;
				else if( !cbIn ) return 0;
				else if( !lpInBuf ) return INVALID_CODE_SIZE;
				_zstm.avail_in = (uInt)cbIn;
				_zstm.next_in = (Bytef*)lpInBuf;
				_zstm.avail_out = (uInt)cbOut;
				_zstm.next_out = (Bytef*)lpOutBuf;
				int zr = deflate( &_zstm, zflag );
				if( zflag == Z_NO_FLUSH ) 
				{
					if( zr != Z_OK ) return INVALID_CODE_SIZE;
				}
				else 
				{
					if( zr != Z_STREAM_END ) return INVALID_CODE_SIZE;
				}
				cbOut = cbOut - _zstm.avail_out;
				return cbIn - _zstm.avail_in;

			}
		};

	};



};



#endif